package com.jin.webvideo.util.cloneUtil;

import com.alibaba.fastjson.JSONObject;

import java.io.*;
import java.lang.reflect.*;
import java.util.*;

public class CloneUtil {
    private static Map<Object, Object> cahce;
    public static<T> T clone(T t){
        cahce = new HashMap<>();
        return copy(t);
    }

    private static<T> T copy(T t) {
        if (t == null){
            return null;
        }
        Class clazz = t.getClass();
        // 基本数据类型及其包装类, 直接返回不用clone
        if (isWrapClass(clazz) || clazz.isPrimitive() || clazz == String.class){
            return t;
        }
        // 如果缓存中存在则无需新建（防止循环依赖）
        Object rst = cahce.get(t);
        if (rst != null){
            return (T) rst;
        }
        // 数组类型调用copyArray进行clone
        if (clazz.isArray()) {
            Object obj = copyArray(t);
            cahce.put(t, obj);
            return (T)obj;
        }
        if (Collection.class.isAssignableFrom(clazz)){
            Object obj = copyCollection((Collection) t);
            cahce.put(t, obj);
            return (T) obj;
        }
        if (Map.class.isAssignableFrom(clazz)){
            Object obj = copyMap((Map) t);
            cahce.put(t, obj);
            return (T) obj;
        }
        // 类上带有Cloneable注解使用反射进行clone
        if (null != clazz.getAnnotation(Cloneable.class)){
            return reflectCopy(t);
        }
        // 入缓存保证指向同一个引用
        // 实现了Serializable接口的使用jdk的Serializable进行clone
        if (Serializable.class.isAssignableFrom(clazz)){
            T obj = jdkSerializableCopy(t);
            cahce.put(t, obj);
            return obj;
        }
        // 其他情况使用JSON进行clone
        T obj = JSONSerializableCopy(t);
        cahce.put(t, obj);
        return obj;
    }

    /**
     * @brif 利用反射复制每一项非静态非final的变量
     * @param t
     * @param <T>
     * @return
     * @throws CloneNotSupportedException
     */
    private static<T> T reflectCopy(T t) {
        Class clazz = t.getClass();
        Cloneable cloneable = (Cloneable) clazz.getAnnotation(Cloneable.class);
        String[] ignore = cloneable.ignore();
        Object rst = null;
        try {
            rst = clazz.getDeclaredConstructor().newInstance();
            // 入缓存防止循环依赖，且保证指向同一个引用
            cahce.put(t, rst);
            while (clazz != Object.class){
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields){
                    if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())
                            || isIgnore(field, ignore)){
                        continue;
                    }
                    field.setAccessible(true);
                    Object val = field.get(t);
                    val = copy(val);
                    field.set(rst, val);
                    field.setAccessible(false);
                }
                clazz = clazz.getSuperclass();
            }
        } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
            throw new RuntimeException("Clone Not Supported: " + e.getMessage());
        }
        return (T)rst;
    }

    private static boolean isIgnore(Field field, String[] ignore){
        for (String temp : ignore){
            if (field.getName().equals(temp)){
                return true;
            }
        }
        return false;
    }
    /**
     * @brif 利用json的序列化与反序列化实现clone
     * @param t
     * @param <T>
     * @return
     */
    public static<T> T JSONSerializableCopy(T t){
        String json = JSONObject.toJSONString(t);
        return JSONObject.parseObject(json, (Type) t.getClass());
    }

    /**
     * @brif 利用Serializable的序列化与反序列化实现clone
     * @param obj
     * @param <T>
     * @return
     * @throws CloneNotSupportedException
     */
    public static<T> T jdkSerializableCopy(T obj) {
        Object rst = null;
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ByteArrayInputStream inputStream = null;
        ObjectOutputStream stream = null;
        try {
            stream = new ObjectOutputStream(outputStream);
            stream.writeObject(obj);
            inputStream = new ByteArrayInputStream(outputStream.toByteArray());
            ObjectInputStream stream1 = new ObjectInputStream(inputStream);
            rst = stream1.readObject();
        } catch (IOException | ClassNotFoundException e) {
            throw new RuntimeException("Clone Not Supported: " + e.getMessage());
        }
        finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (inputStream != null){
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return (T)rst;
    }

    /**
     * @brif 是否为基本类型的包装类
     * @param clazz
     * @return
     */
    private static boolean isWrapClass(Class clazz){
        try {
            Field field = clazz.getField("TYPE");
            return Modifier.isStatic(field.getModifiers())
                    && Modifier.isFinal(field.getModifiers())
                    && ((Class)field.get(null)).isPrimitive();
        } catch (NoSuchFieldException | IllegalAccessException e) {
        }
        return false;
    }

    /**
     * @brif 数组克隆
     * @param array
     * @return
     * @throws CloneNotSupportedException
     */
    public static<T> T copyArray(T array) {
        if (array == null){
            return null;
        }
        int len = Array.getLength(array);
        Class<?> componentType = array.getClass().getComponentType();
        // 基本类型的一维数组
        if (componentType.isPrimitive() || componentType == String.class){
            return (T) primitiveArray(array, len);
        }
        Object rst = Array.newInstance(componentType, len);
        // 非基本类型的一维数组
        if (!componentType.isArray()) {
            for (int i = 0; i < len; i ++){
                Array.set(rst, i , copy(Array.get(array, i)));
            }
            return (T) rst;
        }
        // 多维数组
        for (int i = 0; i < len; i ++){
            Array.set(rst, i , copyArray(Array.get(array, i)));
        }
        return (T) rst;
    }

    /**
     * @brif 基本数据类型数组的克隆
     * @param array
     * @param len
     * @return
     */
    private static Object primitiveArray(Object array, int len){
        Class<?> componentType = array.getClass().getComponentType();
        if (componentType == byte.class){
            return Arrays.copyOf((byte[]) array, len);
        }
        if (componentType == boolean.class){
            return Arrays.copyOf((boolean[]) array, len);
        }
        if (componentType == char.class){
            return Arrays.copyOf((char[]) array, len);
        }
        if (componentType == short.class){
            return Arrays.copyOf((short[]) array, len);
        }
        if (componentType == int.class){
            return Arrays.copyOf((int[]) array, len);
        }
        if (componentType == float.class){
            return Arrays.copyOf((float[]) array, len);
        }
        if (componentType == long.class){
            return Arrays.copyOf((long[]) array, len);
        }
        if (componentType == double.class){
            return Arrays.copyOf((double[]) array, len);
        }
        if (componentType == String.class){
            return Arrays.copyOf((String[]) array, len);
        }
        return null;
    }

    /**
     * @bref 利用Iterator实现Collection的clone
     * @param collection
     * @return
     * @throws CloneNotSupportedException
     */
    public static<E> Collection<E> copyCollection(Collection<E> collection) {
        Iterator iterator = collection.iterator();
        Collection rst = null;
        try {
            rst = collection.getClass().newInstance();
            while (iterator.hasNext()){
                Object obj = iterator.next();
                rst.add(copy(obj));
            }
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException("Clone Not Supported: " + e.getMessage());
        }
        return rst;
    }

    /**
     * @bref map实现深拷贝
     * @param map
     * @param <T>
     * @param <V>
     * @return
     * @throws CloneNotSupportedException
     */
    public static<T, V> Map<T, V> copyMap(Map<T, V> map) {
        Map rst = null;
        try {
            rst = map.getClass().newInstance();
            for (Object obj : map.keySet()) {
                Object key = copy(obj);
                Object val = copy(map.get(obj));
                rst.put(key, val);
            }
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException("Clone Not Supported: " + e.getMessage());
        }
        return rst;
    }
}
